/*
 * PumpController.c
 *
 *  Created on: May 14, 2009
 *      Author: David
 */
#include <WProgram.h>
#include "PumpController.h"
#include "DS1820.h"
#include "LedControl.h"
#include <avr/pgmspace.h>
#include <ctype.h>
#include <avr/eeprom.h>
#include <stdio.h>

/*
 * PumpController.c
 *
 *  Created on: May 14, 2009
 *      Author: David
 */
/* Monitor and report temperature sensors and control the
 * solar water heater. There can be up to 8 temperature
 * sensors, whose addresses are recorded in the eeprom memory.
 * They are recorded in this order:
 *
 * Sensor #     Usage
 * --------     --------------------------------
 *      1       Top of hot water tank
 *      2       Bottom of hot water tank
 *      3       Input to heat exchanger
 *      4       Return from heat exchanger
 *      5       Solar Collector Panel #1
 *      6       Solar Collector Panel #2
 *      7       Top of auxiliary water tank
 *      8       Bottom of auxiliary water tank
 *
 */
#define VERSION          "Version 3.0 compiled on "__DATE__" at " __TIME__
#define RIGHT_PIN       11
#define LEFT_PIN        12
#define LED_PIN         13
#define PV_PANEL_PIN	0
#define BATTERY_PIN     1
#define PUMP_PIN        5
#define MAX_DESIRED_TEMPERATURE 150
#define MIN_USEFUL_VOLTS 65 //times ten
#define DELAYTIME       500
#define DELAYTIME2      60
#define DELAYTIME3      40
#define BUTTON_DEBOUNCE 20
#define DISP_MAX_TEMP   200
#define DISP_MIN_TEMP   50
#define DISP_MAX_VOLTS  20
#define DISP_MIN_VOLTS  0
#define DISP_SCROLL     false
#define DISP_CHART      true
extern char** sensorAddrs;

/*
 We need an LedControl to work with.
 pin 2 is connected to the DataIn
 pin 4 is connected to the CLK
 pin 3 is connected to LOAD
 We have two MAX7221 chips.
 */
LedControl lc=LedControl(2,4,3,2);

boolean heartbeat=LOW;
boolean current_line_is_blank = false; //web interface
boolean tempSelect=false;     //used for multi-char input queries
byte divisor=0;
uint8_t savedAddrs[MAX_TEMPERATURE_SENSORS][ADDRESS_BYTE_SIZE];
char tempBuf[]={"123.45"};
float tempFloat[]={0,0};
char msg[20]={""};
char scratchpad[25];
//uint8_t scratchpad2[25];
int sensorCount=0;
int panel1Temp=0;
int panel2Temp=0;
int topTemp=0;
int bottomTemp=0;
int exchInTemp=0;
int exchRetTemp=0;
int batteryVolts=0; //last battery reading x10
int pvVolts=0;      //last PV panel reading x10
int job=0;          //what we're doing in the loop

#define ISR_FREQ 255    // Sets the speed of the ISR - LOWER IS FASTER
// prescaler is /128 -  125,000/ISR_FREQ+1 (i.e 249=500Hz, 190=650Hz)
// Tweaked depending on the overhead in the ISR & and other factors in the sketch
// Display will be slow if too FAST. Sketch will hang on delay() if way too fast!

#define GREEN 1          // The address of the MAX7221 for the green leds
#define RED 0            // The address of the MAX7221 for the red leds

//To get the voltages correct, we have to divide the input reading by a number.
//The actual divisor used will be calculated based on input from the user.
#define EPROM_BATTERY_DIVISOR_ADDR 50
#define EPROM_PV_DIVISOR_ADDR EPROM_BATTERY_DIVISOR_ADDR+sizeof(float)
float batteryAdjustment=0;
float pvAdjustment=0;

int maxInShutdown=GREEN; // tells which MAX7221 is currently off
unsigned long ISRTime;   // DEBUG to test how long in ISR
int activeColor=GREEN;	 // Start with green initially
boolean displayType=DISP_SCROLL; //start with scrolling display type

// Matrix definitions
#include "matrix.h"

// --- Pump running ---
byte p4[8]={B00000000,
            B00000000,
            B00000000,
            B00000000,
            B11111111,
            B00000000,
            B00000000,
            B00000000};
byte p3[8]={B10000000,
            B01000000,
            B00100000,
            B00010000,
            B00001000,
            B00000100,
            B00000010,
            B00000001};
byte p2[8]={B00001000,
            B00001000,
            B00001000,
            B00001000,
            B00001000,
            B00001000,
            B00001000,
            B00001000};
byte p1[8]={B00000000,
            B00000001,
            B00000010,
            B00000100,
            B00001000,
            B00010000,
            B00100000,
            B01000000};


void setup()
  {
  //Make sure the pump is off
  pinMode(PUMP_PIN, OUTPUT);
  digitalWrite(PUMP_PIN, LOW);

  //Set up the serial communications
  Serial.begin(19200);
  Serial.println("");
  Serial.println(VERSION);

  // The MAX7221s are in power-saving mode on startup,
  // wake them up and initialize them
  Serial.println("Initializing display...");
  lc.clearDisplay(RED);
  lc.clearDisplay(GREEN);
  lc.setIntensity(GREEN,15); // 0 = dim, 15 = full brightness
  lc.setIntensity(RED,8);    // red needs less brightness
  setISRtimer();             // setup the timer
  startISR();                // start the timer to toggle shutdown

  //Set the analog pins to inputs
  Serial.println("Initializing voltage monitors...");
  pinMode(BATTERY_PIN, INPUT);
  digitalWrite(BATTERY_PIN, LOW);
  pinMode(PV_PANEL_PIN, INPUT);
  digitalWrite(PV_PANEL_PIN, LOW);
  analogReference(INTERNAL); //use the 1.1v internal bandgap reference

  //initialze the sensors
  Serial.println("Initializing temperature sensors...");
  temperatureSetup();
  pinMode(LED_PIN, OUTPUT);  //we'll use the led to output a heartbeat
  pinMode(LEFT_PIN,INPUT);   //Button S1
  pinMode(RIGHT_PIN,INPUT);  //Button S2

  //load the voltage tuning values from eeprom
  loadDivisorsFromEprom();

  //Load all of the sensor addresses from eeprom to RAM
  //to make access easier
  if (!loadAddrsFromEprom())
    while(true)
      {
      activeColor=RED;
      strcpy(msg,"Config Error!");
      scroll(msg);
      readSerial();
      scroll(msg);
      delay(DELAYTIME*2);
      }
  else
    printSavedAddresses();

  //Ready to loop
  Serial.println("Ready.");
  Serial.println("");
  }

void toAscii(uint8_t* bytes, char* buf)
  {
  sprintf(buf,"%02x%02x%02x%02x%02x%02x%02x%02x", bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7]);
  }

void storeAddress(int sensorNumber, char* sensorAddress)
  {
  for (int i=0;i<ADDRESS_BYTE_SIZE;i++)
    {
    char first=toupper(sensorAddress[i*2]);
    char second=toupper(sensorAddress[i*2+1]);
    if (first>='A') first-=7;
    if (second>='A') second-=7;
    byte val=(first-'0')<<4|(second-'0');
    uint8_t* addr=(uint8_t*)((sensorNumber-1)*ADDRESS_BYTE_SIZE+i);

//    Serial.print("");
//    Serial.print("Writing '");
//    Serial.print(val,HEX);
//    Serial.print("' to eeprom address ");
//    Serial.println((int)addr,DEC);
    eeprom_write_byte(addr,val);
    }
  }

//Accept a voltage divisor and write it to the appropriate spot in eeprom
void storeDivisor(int pin, float divisor)
  {
  Serial.print("Storing ");
  tempToAscii(divisor,scratchpad);
  Serial.println(scratchpad);
  int addr=pin==BATTERY_PIN?EPROM_BATTERY_DIVISOR_ADDR:EPROM_PV_DIVISOR_ADDR;

  while (!eeprom_is_ready())
    delay(DELAYTIME3);
  eeprom_write_block(&divisor,(void*)addr,sizeof(float));
  }

//Returns 1 if left button is pressed, 2 if the right button
//is pressed, 3 if both are pressed, and 0 if neither is pressed.
int readButtons()
  {
  int val=0;
  if (digitalRead(LEFT_PIN)==LOW) //Left pressed?
    {
    delay(BUTTON_DEBOUNCE);         //wait, are we sure?
    if (digitalRead(LEFT_PIN)==LOW) //Do a double-take
      {
      while(digitalRead(LEFT_PIN)==LOW)
        delay(BUTTON_DEBOUNCE);     //wait for release
      val=1;
      }
    }
  if (digitalRead(RIGHT_PIN)==LOW) //Right pressed?
    {
    delay(BUTTON_DEBOUNCE);        //wait, are we sure?
    if (digitalRead(RIGHT_PIN)==LOW) //Do a double-take
      {
      while(digitalRead(RIGHT_PIN)==LOW)
        delay(BUTTON_DEBOUNCE);     //wait for release
      val+=2;
      }
    }
  if (val==3) //both buttons?
    {
    displayType=!displayType; //switch to or from scrolling mode
//    digitalWrite(LED_PIN,LOW);
    }
//  else if (val!=0)
//    digitalWrite(LED_PIN,HIGH);

  return val;
  }


int shiftLeft(byte* slice)
	{
	readSerial();  //try to make it more responsive to queries
	slice[0]=slice[1];
	slice[1]=slice[2];
	slice[2]=slice[3];
	slice[3]=slice[4];
	slice[4]=slice[5];
	slice[5]=slice[6];
	slice[6]=slice[7];
        return readButtons();
	}

//Scroll msg on the matrix.  Returns zero if no button pressed.
//Returns 1 or -1 if the forward or backward button is pressed.

int scroll(char* msg)
  {
  digitalWrite(LED_PIN, HIGH);
  lc.clearDisplay(GREEN);
  lc.clearDisplay(RED);
  byte slice[8]={0,0,0,0,0,0,0,0};
  int i=0;
  int buttons=0;
  boolean ended=false;
  boolean killed=false;
  while (msg[i]!=0 || !ended)
    {
    for (int j=5;j>=0;j--)
      {
      buttons=shiftLeft(slice);
      if (buttons!=0)
        {
        killed=true;
        break;
        }
      if (msg[i]!=0 && msg[i]!=' ')
        slice[7]=alph[toupper(msg[i])-'!'][j];
      else
        slice[7]=0;

      for (int k=0;k<8;k++)
        lc.setColumn(activeColor,k,slice[k]);
      delay(DELAYTIME3);
      }
    if (killed) break;
    if (msg[i]!=0)
      i++;
    else
      ended=true;
    }
  if (killed)
    lc.clearDisplay(activeColor);
  else
    {
    lc.setColumn(activeColor,0,0); //clear the last bit at the end
    lc.setColumn(activeColor,1,0); //clear the last bit at the end
//    digitalWrite(LED_PIN, LOW);
    }
  return buttons;
  }


//Show the supplied value in the supplied column on the display, as if
//it were a bar graph.
int graph(int column, int value)
  {
  float val=(float)value;
  if (column < 2) //displaying a voltage?
    val=val/(DISP_MAX_VOLTS/8); //stay on the scale
  else
    val=(val-DISP_MIN_TEMP)/((DISP_MAX_TEMP-DISP_MIN_TEMP)/8);

  value=(int)(val+.5); //round to nearest digit
  value=255>>(8-value);       //convert to bar chart
  if (value==0)
    value=1;               //show at least one LED
  lc.setColumn(activeColor, column, value);
  lc.setColumn(activeColor==RED?GREEN:RED,column,0);//clear the non-active color in this column
  return readButtons();
  }


int isCharging() //return non-zero (true) if battery is (should be) charging
  {
  if (pvVolts-5 >= batteryVolts) //-5 for diode drop
    return true;
  else
    return false;
  }

//print the chosen device's voltage to the serial port
void printVolts(int pin)
  {
  int vnum=analogRead(pin);
  double volts=(double)(((float)vnum)/(pin==BATTERY_PIN?batteryAdjustment:pvAdjustment));
  tempToAscii(volts/10,scratchpad);
  Serial.print(scratchpad);
  Serial.print("V ");
  Serial.println(pin==BATTERY_PIN?(isCharging()?"+":"-"):"");
  }

int showVoltage(int pin)
  {
  int vnum=analogRead(pin);
//	Serial.println(vnum);
  int volts=0;
  int cursor=0;
  int column=0; //in case we're not scrolling

  if (pin==BATTERY_PIN)
    {
    column=0;
    volts=(int)((float)vnum)/batteryAdjustment;
    batteryVolts=volts;
    msg[cursor++]='B';
    msg[cursor++]='A';
    msg[cursor++]='T';
    //show battery voltage green if battery is charging,
    //red otherwise
    activeColor=isCharging()?GREEN:RED;

    //if battery is discharging or less than 10.0 volts, reduce the LED current
    if (!isCharging() || volts < 100)
      {
      lc.setIntensity(GREEN,4); // 0 = dim, 15 = full brightness
      lc.setIntensity(RED,2);   // red needs less brightness
      }
    else
      {
      lc.setIntensity(GREEN,14); // 0 = dim, 15 = full brightness
      lc.setIntensity(RED,7);   // red needs less brightness
      }
    }
  else if (pin==PV_PANEL_PIN)
    {
    column=1;
    volts=(int)((float)vnum)/pvAdjustment;
    pvVolts=volts;
    msg[cursor++]='P';
    msg[cursor++]='V';
    //show PV voltages under 12 as red, otherwise green
    activeColor=volts<120?RED:GREEN;
    }

  msg[cursor++]=':';


  //Here volts should be the sampled voltage times 10.
  //Break it up into digits and add the decimal point.
  int tens=0;
  int ones=0;
  int tenths=0;

  tens=volts/100;
  ones=volts/10-tens*10;
  tenths=volts-tens*100-ones*10;

  if (tens>0) //blank leading zero
          msg[cursor++]=tens+'0';
  msg[cursor++]=ones+'0';
  msg[cursor++]='.';
  msg[cursor++]=tenths+'0';
  msg[cursor++]='V';
  msg[cursor++]=0;

  if (displayType==DISP_SCROLL)
    return (scroll(msg));
  else
    return graph(column,volts/10);
  }

int showTemperature(int sensor)
	{
        int hund=0;
        int tens=0;
        int ones=0;
        int cursor=0;
        float tempr=0;
        boolean brr=false;
        boolean error=false;

	if (sensor>0 && sensor<=MAX_TEMPERATURE_SENSORS)
	  {
          // get the temperature
	  tempr=readTemperature(savedAddrs[sensor-1]);
	  if (tempr>-200)
	    {
            tempr=tempr*1.8+32; //convert to Farenheit

            brr=tempr<0;
            if (brr) tempr=0-tempr;

            //break it up into digits
            hund=tempr/100;
            tens=tempr/10-hund*10;
            ones=tempr-hund*100-tens*10;

            //show temperatures over 100 degrees as red, otherwise green
            activeColor=tempr>100?RED:GREEN;
            }
          else
            {
            error=true;
            activeColor=RED;
            }
	  }

        if (sensor==1)
                {
                topTemp=tempr;
                msg[cursor++]='T';
                msg[cursor++]='O';
                msg[cursor++]='P';
                }
        else if (sensor==2)
                {
                bottomTemp=tempr;
                msg[cursor++]='B';
                msg[cursor++]='O';
                msg[cursor++]='T';
                }
        else if (sensor==3)
		{
		exchInTemp=tempr;
		msg[cursor++]='E';
		msg[cursor++]='X';
                msg[cursor++]='/';
                msg[cursor++]='I';
                msg[cursor++]='N';
		}
	else if (sensor==4)
                {
                exchRetTemp=tempr;
                msg[cursor++]='E';
                msg[cursor++]='X';
                msg[cursor++]='/';
                msg[cursor++]='R';
                msg[cursor++]='E';
                msg[cursor++]='T';
                }
	else if (sensor==5)
		{
		panel1Temp=tempr;
		msg[cursor++]='P';
		msg[cursor++]='A';
                msg[cursor++]='N';
                msg[cursor++]='E';
                msg[cursor++]='L';
		msg[cursor++]='1';
		}
	else if (sensor==6)
		{
                panel2Temp=tempr;
		msg[cursor++]='P';
		msg[cursor++]='A';
		msg[cursor++]='N';
                msg[cursor++]='E';
                msg[cursor++]='L';
		msg[cursor++]='2';
		}
	else
		{
		msg[cursor++]='(';
                msg[cursor++]=sensor+'0';
		msg[cursor++]=')';
		}
	msg[cursor++]=':';

	if (!error)
	  {
          //show the minus sign if negative
          if (brr)
                  {
                  msg[cursor++]='-';
                  }

          if (hund>0) //blank leading zero
                  msg[cursor++]=hund+'0';
          msg[cursor++]=tens+'0';
          msg[cursor++]=ones+'0';
          msg[cursor++]='@'; //really degrees symbol
	  }
	else
	  {
	  msg[cursor++]='C';
          msg[cursor++]='R';
          msg[cursor++]='C';
	  }
	msg[cursor++]=0;
        if (displayType==DISP_SCROLL)
          return (scroll(msg));
        else
          return graph(sensor+1,tempr);
	}

void printPumpStatus()
  {
  Serial.print("Pump: ");
  Serial.println(digitalRead(PUMP_PIN)==LOW?"off":"on");
  }

void printSavedAddresses()
  {
  Serial.println("ADDRESSES:");
  for (int i=0;i<sensorCount;i++)
    {
    toAscii(savedAddrs[i],msg);
    Serial.println(msg);
    }
  Serial.println(":END");
  }

void inputVolts()
  {
  Serial.println("Enter the battery voltage to one decimal place:");
  boolean valid=getFour();
  Serial.println("");
  if (valid) //then they input a valid number
    {
    float adj=atof(scratchpad);
    int vnum=analogRead(BATTERY_PIN);
    batteryAdjustment=vnum/(adj*10);
    storeDivisor(BATTERY_PIN,batteryAdjustment);
    }
  Serial.println("Enter the PV panel voltage to one decimal place:");
  valid=getFour();
  Serial.println("");
  if (valid) //then they input a valid number
    {
    float adj=atof(scratchpad);
    int vnum=analogRead(PV_PANEL_PIN);
    pvAdjustment=vnum/(adj*10);
    storeDivisor(PV_PANEL_PIN,pvAdjustment);
    }
  }

//put a four character string representing a float in the scratchpad.
//returns true if at least two numbers and a period have been entered.
boolean getFour()
  {
  int j=0;
  boolean gotDot=false;
  while (j<4)
    {
    while (Serial.available()==0) //wait for input
      delay(100);
    byte val = Serial.read(); //Read the next input char
    Serial.write(val);        //echo it back
    if (val=='\r')            //carriage return
      break;
    else if (val=='\b')       //backspace
      j--;
    else if ((val < '0' || val>'9') && val!='.')//must be numeric
      {
      Serial.println('\a');   //bell
      Serial.println('\b');   //backspace
      continue;
      }
    else scratchpad[j]=val;
    if (val=='.')
      gotDot=true;
    j++;
    }
  return j>2 && gotDot;
  }

//Prompt and read some address strings from the serial port
void inputAddrs()
  {
  for (int i=0;i<MAX_TEMPERATURE_SENSORS;i++)
    {
    Serial.print("Enter the address for sensor #");
    Serial.print(i+1);
    Serial.print(" (");
    switch (i)
      {
      case 0:
        Serial.print("top of tank");
        break;
      case 1:
        Serial.print("bottom of tank");
        break;
      case 2:
        Serial.print("exchanger input");
        break;
      case 3:
        Serial.print("exchanger return");
        break;
      case 4:
        Serial.print("panel #1");
        break;
      case 5:
        Serial.print("panel #2");
        break;
      case 6:
        Serial.print("aux #1");
        break;
      case 7:
        Serial.print("aux #2");
        break;
      default:
        break;
      }
    Serial.println("):");
    int j=0;
    while (j<ADDRESS_SIZE-1)
      {
      while (Serial.available()==0) //wait for input
        delay(100);
      byte val = Serial.read(); //Read the next input char
      Serial.write(val);        //echo it back
      if (val=='\r')            //carriage return
        break;
      else if (val=='\b')       //backspace
        j--;
      else if (toupper(val)>'F')//Hex F
        {
        Serial.println('\a');   //bell
        Serial.println('\b');   //backspace
        continue;
        }
      else scratchpad[j]=val;
      j++;
      }
    Serial.println("");
    if (j==ADDRESS_SIZE-1) //then they input the whole number
      {
      storeAddress(i+1,scratchpad);
      }
    }
  loadAddrsFromEprom();
  printSavedAddresses();
  }

void printTemperature(int sensorNumber)
  {
  tempToAscii(readTemperature(savedAddrs[sensorNumber-1])*1.8+32,tempBuf);
  Serial.println(tempBuf);
  }

void readSerial()
  {
  // Check for any incoming data and respond accordingly. All commands
  // are in lowercase unless they can potentially change something.
  if (Serial.available()>0)
    {
    byte val = Serial.read();

    if (tempSelect)
      {
      if (val>'0' && val<='9')
        {
        printTemperature(val-'0');
        }
      tempSelect=false;
      }
    else if (val=='v')
      Serial.println(VERSION);
    else if (val=='t')
      tempSelect=true;
    else if (val=='l')
      {
      listDevices();
      }
    else if (val=='A') //Adjust voltages
      inputVolts();
    else if (val=='U') //Upload sensor addresses
      inputAddrs();
    else if (val=='d') //Download sensor addresses
      printSavedAddresses();
    else if (val=='b') //Show battery voltage
      printVolts(BATTERY_PIN);
    else if (val=='s') //Show solar PV voltage
      printVolts(PV_PANEL_PIN);
    else if (val=='p') //Show pump status
      printPumpStatus();
    else if (val=='a') //show everything
      {
      Serial.println("");
      Serial.println(VERSION);
      Serial.print("Battery: ");
      printVolts(BATTERY_PIN);
      Serial.print("PV panel: ");
      printVolts(PV_PANEL_PIN);
      Serial.print("Tank top: ");
      printTemperature(1);
      Serial.print("Tank bottom: ");
      printTemperature(2);
      Serial.print("Exchanger in: ");
      printTemperature(3);
      Serial.print("Exchanger return: ");
      printTemperature(4);
      Serial.print("Panel 1: ");
      printTemperature(5);
      Serial.print("Panel 2: ");
      printTemperature(6);
      Serial.print("Pump: ");
      Serial.println(digitalRead(PUMP_PIN)==LOW?"off":"on");
      Serial.println(":END");
      }
    else
      {
      Serial.println("");
      Serial.print("\"");
      Serial.print(val);
      Serial.println("\" is not valid.");
      Serial.println("Valid entries are:");
      Serial.println("A - Adjust voltage mulitplier");
      Serial.println("b - Show battery voltage");
      Serial.println("d - Download sensor addresses");
      Serial.println("l - List attached device addresses");
      Serial.println("p - Show pump status");
      Serial.println("s - Show solar PV panel voltage");
      Serial.println("tn- Display temperature from sensor n");
      Serial.println("U - Upload sensor addresses");
      Serial.println("");

      }
    }
  }

/*
 This method will display a spinning pump icon.
 */
void showPumpRunning()
  {
  for (int j=0;j<4;j++)
    {
    delay(DELAYTIME2);
    for (int i=0;i<8;i++)
            lc.setColumn(activeColor,i,p1[i]);

    delay(DELAYTIME2);
    for (int i=0;i<8;i++)
            lc.setColumn(activeColor,i,p2[i]);

    delay(DELAYTIME2);
    for (int i=0;i<8;i++)
            lc.setColumn(activeColor,i,p3[i]);

    delay(DELAYTIME2);
    for (int i=0;i<8;i++)
            lc.setColumn(activeColor,i,p4[i]);
    }
  }

//check for a bad sensor. If any is found, stop the pump, display
//an error on the matrix, and return false.
boolean sensorsOK()
  {
  boolean ok=true;
  if (   panel1Temp < -190
      || panel2Temp < -190
      || topTemp    < -190
      || bottomTemp < -190
      || exchInTemp < -190
      || exchRetTemp< -190)
    {
    ok=false;
    digitalWrite(PUMP_PIN, LOW);        //pump off
    int cursor=0;
    msg[cursor++]='S';
    msg[cursor++]='E';
    msg[cursor++]='N';
    msg[cursor++]='S';
    msg[cursor++]='O';
    msg[cursor++]='R';
    msg[cursor++]=' ';
    msg[cursor++]='E';
    msg[cursor++]='R';
    msg[cursor++]='R';
    msg[cursor++]='O';
    msg[cursor++]='R';
    msg[cursor++]='!';
    msg[cursor++]=0;
    scroll(msg);
    }
  return ok;
  }

//Turn the pump on or off according to the conditions at this time
void controlPump()
  {
  if (((min(panel1Temp,panel2Temp)>100          //If panels are hot enough
      &&min(panel1Temp,panel2Temp)>bottomTemp+5)//and tank at least 5 deg. colder than panels
      ||(exchInTemp > bottomTemp+10))           //or the heat exchanger is still heating the bottom of the tank
      && bottomTemp<MAX_DESIRED_TEMPERATURE     //and tank not hot enough yet
      && max(batteryVolts,pvVolts)>MIN_USEFUL_VOLTS//and we have enough juice available
      && sensorsOK())                           //and no bad sensors
    digitalWrite(PUMP_PIN, HIGH);               //then pump on
  else
    digitalWrite(PUMP_PIN, LOW);                //otherwise, pump off
  }

//check for incoming commands, and decide if pump should be on.
void readAndControl()
  {
  readSerial();
  controlPump();
  }

void loop()
  {
  readAndControl();
  int lastButton=0;
  switch (job)
    {
    case 0:
      if (digitalRead(PUMP_PIN)==HIGH)
        if (displayType==DISP_SCROLL)
          showPumpRunning();
        else
          digitalWrite(LED_PIN, LOW); //turn on blue LED
      else
        digitalWrite(LED_PIN, HIGH); //turn off blue LED
      break;
    case 1:
    case 2:
    case 3:
    case 4:
    case 5:
    case 6:
    case 7:
      if (job<=sensorCount)
        {
        lastButton=showTemperature(job);
        if (lastButton==1)//left button?
          job-=2;
        if (job<0)
          job=8;
        }
      break;
    case 8:
      lastButton=showVoltage(BATTERY_PIN);
      if (lastButton==1)//left button?
        job=sensorCount-1;
      break;
    case 9:
      lastButton=showVoltage(PV_PANEL_PIN);
      if (lastButton==1)//left button?
        job-=2;
      break;
    default:
      if (job<-1 || job>9)
        job=-1; //will go to zero before next pass
      break;
    }
  job++;
  }


//Show the addresses of all sensors on the display.
//void showSensorAddrs()
//  {
//  for (int sensor=1;sensor<=MAX_TEMPERATURE_SENSORS;sensor++)
//    {
//    if (sensor<=sensorCount)
//      {
//      char* addr=savedAddrs[sensor];
//      int i=0;
//      for (i = 0; i < ADDRESS_SIZE; i++) msg[i] = addr[i];
//      msg[i++]=',';
//      msg[i]=0;
//      scroll(msg);
//      delay(delaytime);
//      }
//    }
//  }

//The addresses of all pre-defined sensors are stored in the
//eeprom as zero-terminated strings.  Pull them out and put them
//into a char array for easier access.
bool loadAddrsFromEprom()
  {
  while (!eeprom_is_ready())
    delay(DELAYTIME3);

  sensorCount=0;

  //see if there's at least one out there.  Return false if not.
  if (eeprom_read_byte((uint8_t*)0)!=0x10) //all sensor addrs start with 10 hex
    return false;

  bool endOfData=false;
  for (int sensorNum=0;sensorNum<MAX_TEMPERATURE_SENSORS;sensorNum++)
    {
    //see if we went past the last one. All sensor addresses begin
    //with 10 hex.
    if (eeprom_read_byte((uint8_t*)(sensorNum*ADDRESS_BYTE_SIZE))!=0x10)
      endOfData=true;

    for (int digit=0;digit<ADDRESS_BYTE_SIZE;digit++)
      {
      if (endOfData)
        savedAddrs[sensorNum][digit]=0;
      else
        savedAddrs[sensorNum][digit]=eeprom_read_byte((uint8_t*)(sensorNum*ADDRESS_BYTE_SIZE+digit));
      }
    if (!endOfData)
      {
      sensorCount++;
//      toAscii(savedAddrs[sensorNum],msg);
//      Serial.println(msg);
      }
    }
  return true;
  }

//Load the voltage adjustments that are stored in eeprom.
void loadDivisorsFromEprom()
  {
  while (!eeprom_is_ready())
    delay(DELAYTIME3);

  eeprom_read_block(&batteryAdjustment,(void*)EPROM_BATTERY_DIVISOR_ADDR,sizeof(float));
  Serial.print("Battery divisor is ");
  if (batteryAdjustment<4 || batteryAdjustment > 5)
    {
    batteryAdjustment=4; //don't want any divide by zero errors
    Serial.println("invalid, using 4.0");
    }
  else
    {
    tempToAscii(batteryAdjustment,scratchpad);
    Serial.println(scratchpad);
    }

  int x=EPROM_PV_DIVISOR_ADDR; //compiler complains if we don't do it this way
  eeprom_read_block(&pvAdjustment,(void*)x,sizeof(float));
  Serial.print("PV divisor is ");
  if (pvAdjustment<4 || pvAdjustment > 5)
    {
    pvAdjustment=4; //don't want any divide by zero errors
    Serial.println("invalid, using 4.0");
    }
  else
    {
    tempToAscii(pvAdjustment,scratchpad);
    Serial.println(scratchpad);
    }
  }

/*
void readSerial()
  {
  if (Serial.available()>0)
     {
     byte c = Serial.read();
     // if we're at the end of the line (received a newline
     // character) and the line is blank, the http request has ended,
     // so we can send a reply
     if (c == '\n' && current_line_is_blank)
       {
       char buf[50];
       sprintf(buf,"%s","<html>\r\n");
       getTemperatures(buf+strlen(buf));
       sprintf(buf+strlen(buf),"%s","</html>");
       // send a standard http response header
       Serial.println("HTTP/1.0 200 OK");
       Serial.println("Content-Type: text/html");
       Serial.print("Content-Length: ");
       Serial.println(strlen(buf));
       Serial.println("");

//       printTemperatures();
       Serial.println(buf);
       Serial.println("");
       Serial.println("<br>");
       // output the value of each humidity sensor
       printHumidity();
       Serial.println("");
       Serial.println("<br>");
       }
       else if (c == '\n')
         {
         // we're starting a new line
         current_line_is_blank = true;
         }
       else if (c != '\r')
         {
         // we've a character on the current line
         current_line_is_blank = false;
        }
      }
    }
*/


/*
// this function will return the number of bytes free in RAM
extern int  __bss_end;
extern int  *__brkval;
int freemem()
  {
  int free_memory;
  if((int)__brkval == 0)
    free_memory = ((int)&free_memory) - ((int)&__bss_end);
  else
    free_memory = ((int)&free_memory) - ((int)__brkval);
  return free_memory;
  }
*/

/////////////////////////////ISR Timer Functions ///////////////////////////
//This ISR toggles shutdown between the 2 MAX7221s
ISR(TIMER2_COMPA_vect)
  {
  if(maxInShutdown==RED)
    {
    lc.shutdown(GREEN,true);  // The order is important - shutdown first
    lc.shutdown(RED,false);   // then restart the other.
    maxInShutdown=GREEN;
    }
  else
    {
    lc.shutdown(RED,true);
    lc.shutdown(GREEN,false);
    maxInShutdown=RED;
    }
  }

void setISRtimer(){  // setup ISR timer controling toggling
  TCCR2A = 0x02;     // WGM22=0 + WGM21=1 + WGM20=0 = Mode2 (CTC)
  TCCR2B = 0x05;     // CS22=1 + CS21=0 + CS20=1 = /128 prescaler (125kHz)
  TCNT2 = 0;         // clear counter
  OCR2A = ISR_FREQ;  // set TOP (divisor) - see #define above
}

void startISR(){      // Starts the ISR
  TCNT2 = 0;          // clear counter (needed here also)
  TIMSK2|=(1<<OCIE2A);// set interrupts=enabled (calls ISR(TIMER2_COMPA_vect)
}

void stopISR(){        // Stops the ISR
  TIMSK2&=~(1<<OCIE2A);// disable interrupts
}
